#include "./cmd_uart.h"
#include "../device/gpio.h"
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

namespace driver {

CmdUart cmdUart;

using namespace device;

using Tx = PB<10>; // DMA0_CH1
using Rx = PB<11>; // DMA0_CH2

void CmdUart::init() {

    RCU_APB2EN |= RCU_APB2EN_PBEN;
    RCU_AHBEN |= RCU_AHBEN_DMA0EN;
    RCU_APB1EN |= RCU_APB1EN_USART2EN;

    config_pp_af<Tx>();
    config_float_input<Rx>();

    m_mq.init();
    m_flags.init();
    m_flags.set_flags(FLAG_DMA_TC | FLAG_RES_LOCK);

    USART_CTL0(USART2) = 0x00;
    USART_CTL1(USART2) = 0x00;
    USART_CTL2(USART2) = 0xC0;
    USART_BAUD(USART2) = 60000000 / 115200;
    USART_CTL0(USART2) = 0x201C;

    DMA_INTC(DMA0) = 0x0F << (2 * 4);
    DMA_CH2CTL(DMA0) = 0x00;
    DMA_CH2CNT(DMA0) = RECV_BUF_SIZE;
    DMA_CH2MADDR(DMA0) = reinterpret_cast<uint32_t>(m_recv_buf);
    DMA_CH2PADDR(DMA0) = reinterpret_cast<uint32_t>(&USART_DATA(USART2));
    DMA_CH2CTL(DMA0) = DMA_CHXCTL_MNAGA + DMA_CHXCTL_CMEN + DMA_CHXCTL_CHEN;

    NVIC_EnableIRQ(USART2_IRQn);
    NVIC_EnableIRQ(DMA0_Channel1_IRQn);
}

void CmdUart::write_by_dma(const void *buf, uint16_t size) {
    DMA_INTC(DMA0) =  0x0F << (1 * 4);
    DMA_CH1CTL(DMA0) = 0x00;
    DMA_CH1MADDR(DMA0) = reinterpret_cast<uint32_t>(buf);
    DMA_CH1CNT(DMA0) = size;
    DMA_CH1PADDR(DMA0) = reinterpret_cast<uint32_t>(&USART_DATA(USART2));
    DMA_CH1CTL(DMA0) = DMA_CHXCTL_MNAGA +
        DMA_CHXCTL_DIR +
        DMA_CHXCTL_FTFIE +
        DMA_CHXCTL_CHEN;
}

void CmdUart::on_recv_idle() {
    uint32_t index = RECV_BUF_SIZE - DMA_CH2CNT(DMA0);
    if (index == m_recv_index) {
        return;
    }
    using namespace utils;
    if (index > m_recv_index) {
        ByteView bw(m_recv_buf + m_recv_index, index - m_recv_index);
        m_mq.post(bw);
    } else {
        ByteView bw(m_recv_buf + m_recv_index, RECV_BUF_SIZE - m_recv_index);
        m_mq.post(bw);
        if (index != 0) {
            bw.buf = m_recv_buf;
            bw.len = index;
            m_mq.post(bw);
        }
    }
    m_recv_index = index;
}

void CmdUart::printf(const char *format, va_list ap) {
    m_flags.wait_any_flags(FLAG_DMA_TC);
    using namespace serialize;
    int n = vsprintf(reinterpret_cast<char *>(m_write_buf + 7), format, ap);
    encode_all(m_write_buf, conn::proto::HEAD);
    m_write_buf[2] = n + 9;
    m_write_buf[3] = conn::proto::TYPE_NOTIFY;
    m_write_buf[4] = 0; // seq

    m_write_buf[5] = conn::proto::CMD_LOG;
    m_write_buf[6] = n; // data len
    uint8_t sum = utils::alg::xor_sum(m_write_buf + 3, n + 4);
    m_write_buf[7 + n] = sum;
    m_write_buf[8 + n] = conn::proto::END;
    write_by_dma(m_write_buf, n + 9);
}

}

using namespace driver;

extern "C"
void DMA0_Channel1_IRQHandler() {   
    if (DMA_INTF(DMA0) & (DMA_INTF_FTFIF << (1 * 4))) {
        DMA_INTC(DMA0) = DMA_INTC_FTFIFC << (1 * 4);
        cmdUart.on_dma_tc();
    }
}

extern "C"
void USART2_IRQHandler() {
    if (USART_STAT0(USART2) & USART_STAT0_IDLEF) {
        uint32_t clr = USART_DATA(USART2);
        cmdUart.on_recv_idle();
    }
}





